package neuronvisualizer;

import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.filechooser.FileFilter;

import org.lwjgl.input.Mouse;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Interact2D;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;
import com.threed.jpct.Interact2D;
import com.threed.jpct.util.KeyMapper;
import com.threed.jpct.util.KeyState;



public class Visual extends JFrame implements Runnable{
	private BufferedImage im;
	private Driver parent;
	private boolean takeScreenshot, loadFile;
	private KeyMapper keyMapper = null;
	
	private boolean rotUp,rotDown,rotLeft,rotRight;
	
	//3D SCENE VARIABLES
		private World visualizer;
		
	//CAMERA VARIABLES
		//private SimpleVector ORIGIN = new SimpleVector(0,0,0);
		private final SimpleVector CAMDEFAULT = new SimpleVector(1000,-2000,-2000);
		private SimpleVector defaultMidpoint;
		private SimpleVector currentMidpoint;
		
	//OPEN GL VARIABLES
		private static FrameBuffer buffer;
	
	//OTHER VARIABLES
		private static boolean running;
	
	public Visual(Driver p, SimpleVector m){
		rotUp = rotDown = rotLeft = rotRight = false;
		loadFile = false;
		takeScreenshot = false;
		parent = p;
		defaultMidpoint = m;
		currentMidpoint = defaultMidpoint;
		keyMapper = new KeyMapper();
	}
	
	public void config(){
		Config.glWindowName="Neuron Network Visualizer";
		Config.maxPolysVisible = 100000;
	    Config.glAvoidTextureCopies = true;
        Config.saveMemory = true;
    	Config.glTrilinear = true;
    	Config.fadeoutLight = false;
    	
	}
	
	public void init3D(){
		visualizer=new World();
		visualizer.setAmbientLight(175,175,175);
		visualizer.setClippingPlanes(1f,3000f);
		visualizer.getCamera().setPosition(CAMDEFAULT);
		visualizer.getCamera().lookAt(defaultMidpoint);
		visualizer.addLight(new SimpleVector(defaultMidpoint.x,-300,defaultMidpoint.z),new Color(150,150,150));
		visualizer.setLightAttenuation(0, -3);
		buffer = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_NORMAL);
		buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
		buffer.enableRenderer(IRenderer.RENDERER_OPENGL);
		
	}
	
	public void getModels(){
		for(int i=0; i<4; i++){
			for(int i1=0;i1<5;i1++){
				Object3D secGlow = parent.getSectionGlow(i,i1);
				Object3D secmodel = parent.getSectionModel(i,i1);
				addModel(secmodel);
				addModel(secGlow);
			}
		}
	}
	
	public void addModel(Object3D m){
		if(m!= null)
			visualizer.addObject(m);
	}
	public void removeModel(Object3D m){
		if(m!=null)
			visualizer.removeObject(m);
	}
	
	public void run(){

		config();
		init3D();
		getModels();
		visualizer.buildAllObjects();
		
		System.out.println(im);
		long ticks=0;
		int spark =1;
		int lastSpark=1;
		running = true;
		boolean firstrun = true;
		while (!org.lwjgl.opengl.Display.isCloseRequested() && running) {
			if(takeScreenshot){
				im = (BufferedImage) buffer.getOutputBuffer();
				try{
					JFileChooser fc = new JFileChooser();
					
					fc.setAcceptAllFileFilterUsed(false);
					
					fc.setFileFilter(new FileFilter(){
						public boolean accept(File f) {
					        if (f.isDirectory()) {
					            return true;
					        }
					        String filename = f.getName();
					        
					        if (filename != null) {
					            if (filename.endsWith(".jpg")){
					                    return true;
					            } else {
					                return false;
					            }
					        }
					 
					        return false;
						}

						@Override
						public String getDescription() {
					        return "*.png";
						}
							
					});

					int returnVal = fc.showSaveDialog(null);
					if(returnVal == JFileChooser.APPROVE_OPTION){
						File f = fc.getSelectedFile();
						if(!f.getName().endsWith(".png")){
							f = new File(f.getAbsolutePath()+".png");
						}
						ImageIO.write(im, "png", f);
					}
				}
				catch(IOException e){
					e.printStackTrace();
				}
				takeScreenshot = false;
			}
			if(loadFile){
				loadFile = false;
				getModels();
				visualizer.buildAllObjects();
			}
			ticks++;
			checkMouse();
			checkKeys();
			moveCam();
			
	        buffer.clear(java.awt.Color.BLACK);
			visualizer.renderScene(buffer);
			visualizer.draw(buffer);
			buffer.update();
			buffer.displayGLOnly();
			
			
			//GIVES THE VISUALIZATION ROUGHLY 30 FPS
			try{
				Thread.sleep(33);
			}catch(Exception e){e.printStackTrace();}
			firstrun = false;
		}
		closeVisual();	
	}
	
	public void setLoadFile(){
		loadFile = true;
	}
	public boolean fileIsLoaded(){
		return loadFile;
	}
	public void resetCamera(){
		visualizer.getCamera().setPosition(CAMDEFAULT);
		visualizer.getCamera().lookAt(defaultMidpoint);
	}
	
	public static void closeVisual(){
		running = false;
		try{
			buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
			buffer.dispose();
		}catch(Exception e){e.printStackTrace();}
	}
	public Camera getCamera(){
		return visualizer.getCamera();
	}

	
	public void moveCam(){
		if(rotUp){
			visualizer.getCamera().moveCamera(Camera.CAMERA_MOVEUP,100f);
			visualizer.getCamera().lookAt(currentMidpoint);				
		}
		if(rotDown){
			visualizer.getCamera().moveCamera(Camera.CAMERA_MOVEDOWN, 100f);
			visualizer.getCamera().lookAt(currentMidpoint);		
		}
		if(rotLeft){
			visualizer.getCamera().moveCamera(Camera.CAMERA_MOVELEFT,100f);
			visualizer.getCamera().lookAt(currentMidpoint);			
		}
		if(rotRight){
			visualizer.getCamera().moveCamera(Camera.CAMERA_MOVERIGHT,100f);
			visualizer.getCamera().lookAt(currentMidpoint);			
		}
	}
	
	public void checkKeys(){
		KeyState ks = null;
		while ((ks = keyMapper.poll()) != KeyState.NONE) {
			if (ks.getKeyCode() == KeyEvent.VK_UP  ) {
				rotUp = ks.getState();			
			}
			if (ks.getKeyCode() == KeyEvent.VK_DOWN) {
				rotDown = ks.getState();
			}
			if (ks.getKeyCode() == KeyEvent.VK_LEFT) {
				rotLeft = ks.getState();
			}			
			if (ks.getKeyCode() == KeyEvent.VK_RIGHT) {
				rotRight = ks.getState();
			}
		}

	}
	
	public void checkMouse(){	//use for the rotate button on the optionbar
		boolean cameramoved = false;
		int dx = Mouse.getDX();
		int dy = Mouse.getDY();
		int dw = Mouse.getDWheel();
		
		if(dx!=0 && Mouse.isButtonDown(1)){
			cameramoved = true;
			if(dx<0){
				visualizer.getCamera().moveCamera(Camera.CAMERA_MOVERIGHT,20f);
			}
			else{
				visualizer.getCamera().moveCamera(Camera.CAMERA_MOVELEFT,20f);
			}
			currentMidpoint.x+=Math.cos(dx);
			currentMidpoint.z+=Math.sin(dx);
		}
		if(dy!=0 && Mouse.isButtonDown(1)){
			cameramoved = true;
			if(dy<0){
				visualizer.getCamera().moveCamera(Camera.CAMERA_MOVEUP, 20f);
				currentMidpoint.y-=20f;
			}
			else{
				visualizer.getCamera().moveCamera(Camera.CAMERA_MOVEDOWN,20f);
				currentMidpoint.y+=20f;
			}
		}	
		if(Mouse.isButtonDown(0) && dx==0 && dy==0){
			//Picking code! Get mouse coordinates, resolve into SimpleVector, then determine whether there is a Section at these coordinates. 
			//If there is, expand it.
			int x = Mouse.getX();
			int y = buffer.getOutputHeight() - Mouse.getY();
			SimpleVector dir=Interact2D.reproject2D3D(visualizer.getCamera(), buffer, x, y).normalize();
			int[] res=Interact2D.pickPolygon(visualizer.getVisibilityList(), dir);
			try{
				int resID = res[0];				
				parent.pickedObject(resID);
			}
			catch(NullPointerException e){
			}
		}
		
		if(dw!=0){
			cameramoved = true;
			if(dw>0){
				visualizer.getCamera().moveCamera(Camera.CAMERA_MOVEIN,120f);
			}
			else{
				visualizer.getCamera().moveCamera(Camera.CAMERA_MOVEOUT,120f);
			}
		}		
	}
	public void checkNeurons(){
		for(int i=0;i<parent.getNeuronCount();i++){
		    	Neuron n = parent.getNeuron(i);
			if(n.deleteFlag){
				removeModel(n.getModel());
				removeModel(n.getGlow());
				n.destroyModel();
			}
			else if(n.addFlag){
				addModel(n.getModel());
				addModel(n.getGlow());
				n.modelBuilt();
			}
		}
	}
	public void setScreenshot(){
		takeScreenshot = true;
	}

}

